
/**
 * Copyright 2009 cestum.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
 
package org.waveprotocol.wave.model.document.operation.impl {
import org.waveprotocol.wave.model.document.operation.DocInitialization;
import org.waveprotocol.wave.model.document.operation.DocumentOperation;
import org.waveprotocol.wave.model.document.operation.algorithm.Composer;
import org.waveprotocol.wave.model.document.operation.automaton.AutomatonDocument;
import org.waveprotocol.wave.model.document.operation.automaton.DocOpAutomaton;
import org.waveprotocol.wave.model.document.operation.automaton.DocumentSchema;
import org.waveprotocol.wave.model.document.operation.automaton.NoSchemaConstraints;
import org.waveprotocol.wave.model.document.operation.automaton.ValidationResult;
import org.waveprotocol.wave.model.document.operation.automaton.violation.ViolationCollector;
import org.waveprotocol.wave.model.util.Preconditions;

/**
 * Validates an operation against a document.
 *
 *
 */
public final class DocOpValidator {

  function DocOpValidator() {}
  /**
   * A schema that permits anything
   */
  public static var NO_SCHEMA_CONSTRAINTS:DocumentSchema= new NoSchemaConstraints();

	//For overloaded add methods
    //Not unit tested
	 public static function validate( v:ViolationCollector, schema:DocumentSchema,
      doc:AutomatonDocument, op:DocumentOperation) { 
	     Composer["validate"+arguments.length].apply(arguments);  
	}  
	
  /**
   * Returns whether op is a well-formed document initialization and satisfies
   * the given schema constraints.
   */
  public static function validate3(v:ViolationCollector,
      schema:DocumentSchema, op:DocInitialization):ValidationResult {
    Preconditions.checkNotNull(schema, "Schema constraints required, if not, " +
        "use DocumentSchema.NO_SCHEMA_CONSTRAINTS");
    return validate(v, schema, DocOpAutomaton.EMPTY_DOCUMENT, op);
  }
    //For overloaded add methods
	 public static function isWellFormed(v:ViolationCollector, op:Object):Boolean {  
         // figure out what the type of o actually is and call our  
         // private helper function based on that  
         if (op instanceof DocumentOperation) {  
            return isWellFormed_Docop(v, DocumentOperation(op));  
         } else if ( op instanceof BufferedDocOpImpl) {  
			 return isWellFormed_buffered(v,BufferedDocOpImpl(op));  
         } 
		 return false;
    }  

  /**
   * Returns whether op is well-formed.
   *
   * Any violations recorded in the output v that are not well-formedness
   * violations are meaningless.
   */
  public static function isWellFormed_Docop(v:ViolationCollector, op:DocumentOperation):Boolean {
    if (op instanceof BufferedDocOpImpl) {
      return isWellFormed(v, BufferedDocOpImpl(op));
    } else {
      return isWellFormedRaw(v, op);
    }
  }

  /**
   * Same as {@link #isWellFormed(ViolationCollector, DocOp)}, but with
   * a fast path for already-validated instances of BufferedDocOpImpl
   */
  public static function isWellFormed_buffered(v:ViolationCollector, buffered:BufferedDocOpImpl):Boolean {
    if (buffered.isKnownToBeWellFormed()) {
      // fast path
      return true;
    } else {
      if (isWellFormedRaw(v, buffered)) {
        buffered.markWellFormed();
        return true;
      } else {
        return false;
      }
    }
  }

  /**
   * Same as {@link #isWellFormed(ViolationCollector, DocOp)}, but without
   * the fast path for BufferedDocOpImpl
   */
  public static function isWellFormedRaw(v:ViolationCollector, op:DocumentOperation):Boolean {
    // We validate the operation against the empty document.  It will likely
    // be invalid; however, we ignore the validity aspect anyway since we
    // only care about well-formedness.
    return !validate4(v, NO_SCHEMA_CONSTRAINTS,
        DocOpAutomaton.EMPTY_DOCUMENT, op)
        .isIllFormed();
  }

  
  static var ILL_FORMED:IllFormedError= new IllFormedError(
      "Preallocated exception with a meaningless stack trace");

  /**
   * Returns whether op is well-formed, applies to doc, and preserves the given
   * schema constraints.  Will not modify doc.
   */
  public static function validate4(
      v:ViolationCollector, schema:DocumentSchema,
      doc:AutomatonDocument, op:DocumentOperation):ValidationResult {

    if (schema == null) {
      schema = NO_SCHEMA_CONSTRAINTS;
    }
    var a:DocOpAutomaton= new DocOpAutomaton(doc, schema);
    var accu:Array= [ValidationResult.VALID];
    try {
      op.apply(new IllFormedDocOpCursor(accu, a, v));
    } catch (e:IllFormedError) {
      return ValidationResult.ILL_FORMED;
    }
    accu[0] = accu[0].mergeWith(a.checkFinish(v));
    return accu[0];
  }

}
}